import json
import traceback
import urllib
from typing import Type, TypeVar, Optional, List

from mg_app_framework import HttpBasicHandler, get_logger, MesCode, get_store
from pydantic import ValidationError, BaseModel
from tornado.httputil import HTTPServerRequest

from .encoder import DefaultEncoder
from .biz_exception import BizException, ParamCheckError, PermError

BaseModelType = TypeVar('BaseModelType', BaseModel, BaseModel)


class BaseHandler(HttpBasicHandler):

    request: HTTPServerRequest

    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self._user_info = None
        self._user_info_cookie = None

    def data_received(self, chunk):
        pass

    def get_remote_ip(self):
        return self.request.headers.get("X-Real-IP") or \
               self.request.headers.get("X-Forwarded-For") or \
               self.request.remote_ip

    def options(self, *args, **kwargs):
        self.set_status(200)
        self.set_header('Access-Control-Allow-Headers', 'Access-Control-Allow-Headers, Content-Type, Authorization')
        self.set_header('Access-Control-Allow-Methods', 'DELETE, POST, GET, OPTIONS, PUT')
        self.finish()

    def write_error(self, status_code, **kwargs):
        # 统一记录下500的错误, 这里可以填写出错后返回的信息
        exc_cls, exc_instance, trace = kwargs.get("exc_info")

        if isinstance(exc_instance, BizException):
            self.set_status(200)
            self.send_response_data(MesCode.fail, None, exc_instance.msg)
            return
        elif isinstance(exc_instance, (ParamCheckError, PermError)):
            self.set_status(200)
            self.send_response_data(MesCode.fail, None, exc_instance.msg)
            return

        # 只处理500的错误
        if status_code == 500:
            get_logger().error(traceback.format_exc())
            self.set_status(status_code)
            self.send_response_data(MesCode.fail, None, '服务器内部错误')

    def set_default_headers(self):
        """添加返回content-type是json的标识符, 方便postman调试"""
        super().set_default_headers()
        self.set_header('Access-Control-Allow-Methods', 'PUT,DELETE,OPTIONS,GET,POST')
        self.set_header("Content-Type", "application/json; charset=utf-8")

    def _set_response_data(self, result, encoder=None):
        if encoder is None:
            encoder = DefaultEncoder
        try:
            self.finish(json.dumps(result, ensure_ascii=False, cls=encoder))
        except Exception as e:
            self.finish(json.dumps(
                {"code": MesCode.fail, "info": str(e)}))
            traceback.print_exc()

    def send_response_file(self, filename=None, data=None, buf_size=1024):
        self.set_header('Content-Type', 'application/octet-stream')
        # 设置请求头数据
        self.set_header('Content-Disposition', 'attachment; filename=%s; filename*=%s' %
                        (urllib.parse.quote(filename), urllib.parse.quote(filename)))
        try:
            data.send(buf_size, self)
        except Exception:
            traceback.print_exc()
            get_logger().exception('send_response_file data send wrong')
            get_logger().exception('data type = ' + str(type(data)))
        self.finish()

    def success_response(self, data = None, msg='ok'):
        return self.send_response_data(MesCode.success, data, msg)

    def error_response(self, msg=None, data=None):
        return self.send_response_data(MesCode.fail, data, msg)

    def query_params(self):
        """获取url后的参数"""
        argument_data = dict()
        a = self.request.query_arguments
        for i in a.keys():
            argument_data[i] = self.get_argument(i)
        return argument_data

    def check_params(self, cls: Type[BaseModelType], **kwargs) -> BaseModelType:
        """pydentic校验参数"""
        try:
            obj = cls(**kwargs)
            return obj
        except Exception:
            raise ParamCheckError()

